<?php
/**
 * SmartTemplateDebugger Class
 * Used by SmartTemplate Class
 *
 * @desc Used by SmartTemplate Class
 * @author Philipp v. Criegern philipp@criegern.com
 * @author Manuel 'EndelWar' Dalla Lana endelwar@aregar.it
 * @version 1.2.1 03.07.2006
 *
 * CVS ID: $Id: class.smarttemplatedebugger.php,v 1.5 2006/07/02 22:17:15 endelwar Exp $
 */
class SmartTemplateDebugger {
	/**
	 * The template Filename
	 *
	 * @access private
	 */
	var $filename;
	
	/**
	 * The template itself
	 *
	 * @access private
	 */
	var $template;
	
	/**
	 * SmartTemplateParser Constructor
	 *
	 * @param string $template_filename HTML Template Filename
	 */
	function SmartTemplateDebugger($template_filename) {
		$this->filename = $template_filename;
		
		//	Load Template
		if ($hd = @fopen ( $template_filename, "r" )) {
			$this->template = fread ( $hd, filesize ( $template_filename ) );
			fclose ( $hd );
		} else {
			$this->template = "SmartTemplate Debugger Error: File not found: '$template_filename'";
		}
		$this->tab [0] = '';
		for($i = 1; $i < 10; $i ++) {
			$this->tab [$i] = str_repeat ( '    ', $i );
		}
	}
	
	/**
	 * Main Template Parser
	 *
	 * @param string $compiled_template_filename Compiled Template Filename
	 * @desc Creates Compiled PHP Template
	 */
	function start($vars) {
		$page = $this->template;
		
		$page = preg_replace ( "/(<!-- BEGIN [ a-zA-Z0-9_.]* -->)/", "\n$1\n", $page );
		$page = preg_replace ( "/(<!-- IF .+? -->)/", "\n$1\n", $page );
		$page = preg_replace ( "/(<!-- END.*? -->)/", "\n$1\n", $page );
		$page = preg_replace ( "/(<!-- ELSEIF .+? -->)/", "\n$1\n", $page );
		$page = preg_replace ( "/(<!-- ELSE [ a-zA-Z0-9_.]*-->)/", "\n$1\n", $page );
		
		$page = $this->highlight_html ( $page );
		
		$rows = explode ( "\n", $page );
		$page_arr = array ();
		$level = 0;
		$blocklvl = 0;
		$rowcnt = 0;
		$spancnt = 0;
		$offset = 22;
		$lvl_block = array ();
		$lvl_row = array ();
		$lvl_typ = array ();
		foreach ( $rows as $row ) {
			if ($row = trim ( $row )) {
				$closespan = false;
				if (substr ( $row, $offset, 12 ) == '&lt;!-- END ') {
					if ($level < 1) {
						$level ++;
						$error [$rowcnt] = "END Without BEGIN";
					} elseif ($lvl_typ [$level] != 'BEGIN') {
						$error [$lvl_row [$level]] = "IF without ENDIF";
						$error [$rowcnt] = "END Without BEGIN";
					}
					$blocklvl --;
					$level --;
					$closespan = true;
				}
				if (substr ( $row, $offset, 14 ) == '&lt;!-- ENDIF ') {
					if ($level < 1) {
						$level ++;
						$error [$rowcnt] = "ENDIF Without IF";
					} elseif ($lvl_typ [$level] != 'IF') {
						$error [$lvl_row [$level]] = "BEGIN without END";
						$error [$rowcnt] = "ENDIF Without IF";
					}
					$closespan = true;
					$level --;
				}
				if ($closespan) {
					$page_arr [$rowcnt - 1] .= '</span>';
				}
				$this_row = $this->tab [$level] . $row;
				if (substr ( $row, $offset, 12 ) == '&lt;!-- ELSE') {
					if ($level < 1) {
						$error [$rowcnt] = "ELSE Without IF";
					} elseif ($lvl_typ [$level] != 'IF') {
						$error [$rowcnt] = "ELSE Without IF";
					} else {
						$this_row = $this->tab [$level - 1] . $row;
					}
				}
				if (substr ( $row, $offset, 14 ) == '&lt;!-- BEGIN ') {
					if ($blocklvl == 0) {
						if ($lp = strpos ( $row, '--&gt;' )) {
							if ($blockname = trim ( substr ( $row, $offset + 14, $lp - $offset - 14 ) )) {
								if ($nr = count ( $vars [$blockname] )) {
									$this_row .= $this->toggleview ( "$nr Entries" );
								} else {
									$this_row .= $this->toggleview ( "Emtpy" );
								}
							}
						}
					} else {
						$this_row .= $this->toggleview ( '[' );
					}
					$blocklvl ++;
					$level ++;
					$lvl_row [$level] = $rowcnt;
					$lvl_typ [$level] = 'BEGIN';
				} elseif (substr ( $row, $offset, 11 ) == '&lt;!-- IF ') {
					$level ++;
					$lvl_row [$level] = $rowcnt;
					$lvl_typ [$level] = 'IF';
					$this_row .= $this->toggleview ();
				}
				$page_arr [] = $this_row;
				$lvl_block [$rowcnt] = $blocklvl;
				$rowcnt ++;
			}
		}
		if ($level > 0) {
			$error [$lvl_row [$level]] = "Block not closed";
		}
		
		$page = join ( "\n", $page_arr );
		$rows = explode ( "\n", $page );
		$cnt = count ( $rows );
		
		for($i = 0; $i < $cnt; $i ++) {
			//	Add Errortext
			if (isset ( $error )) {
				if ($err = $error [$i]) {
					$rows [$i] = '<b>' . $rows [$i] . '        ERROR: ' . $err . '!</b>';
				}
			}
			
			//	Replace Scalars
			if (preg_match_all ( '/{([a-zA-Z0-9_. &;]+)}/', $rows [$i], $var )) {
				foreach ( $var [1] as $tag ) {
					$fulltag = $tag;
					if ($delim = strpos ( $tag, ' &gt; ' )) {
						$tag = substr ( $tag, 0, $delim );
					}
					if (substr ( $tag, 0, 4 ) == 'top.') {
						$title = $this->tip ( $vars [substr ( $tag, 4 )] );
					} elseif ($lvl_block [$i] == 0) {
						$title = $this->tip ( $vars [$tag] );
					} else {
						$title = '[BLOCK?]';
					}
					$code = '<b title="' . $title . '">{' . $fulltag . '}</b>';
					$rows [$i] = str_replace ( '{' . $fulltag . '}', $code, $rows [$i] );
				}
			}
			
			//	Replace Extensions
			if (preg_match_all ( '/{([a-zA-Z0-9_]+):([^}]*)}/', $rows [$i], $var )) {
				foreach ( $var [2] as $tmpcnt => $tag ) {
					$fulltag = $tag;
					if ($delim = strpos ( $tag, ' &gt; ' )) {
						$tag = substr ( $tag, 0, $delim );
					}
					if (strpos ( $tag, ',' )) {
						list ( $tag, $addparam ) = explode ( ',', $tag, 2 );
					}
					$extension = $var [1] [$tmpcnt];
					
					if (substr ( $tag, 0, 4 ) == 'top.') {
						$title = $this->tip ( $vars [substr ( $tag, 4 )] );
					} elseif ($lvl_block [$i] == 0) {
						$title = $this->tip ( $vars [$tag] );
					} else {
						$title = '[BLOCK?]';
					}
					$code = '<b title="' . $title . '">{' . $extension . ':' . $fulltag . '}</b>';
					$rows [$i] = str_replace ( '{' . $extension . ':' . $fulltag . '}', $code, $rows [$i] );
				}
			}
			
			//	'IF nnn' Blocks
			if (preg_match_all ( '/&lt;!-- IF ([a-zA-Z0-9_.]+) --&gt;/', $rows [$i], $var )) {
				foreach ( $var [1] as $tag ) {
					if (substr ( $tag, 0, 4 ) == 'top.') {
						$title = $this->tip ( $vars [substr ( $tag, 4 )] );
					} elseif ($lvl_block [$i] == 0) {
						$title = $this->tip ( $vars [$tag] );
					} else {
						$title = '[BLOCK?]';
					}
					$code = '<span title="' . $title . '">&lt;!-- IF ' . $tag . ' --&gt;</span>';
					$rows [$i] = str_replace ( "&lt;!-- IF $tag --&gt;", $code, $rows [$i] );
					if ($title == '[NULL]') {
						$rows [$i] = str_replace ( 'Hide', 'Show', $rows [$i] );
						$rows [$i] = str_replace ( 'block', 'none', $rows [$i] );
					}
				}
			}
		}
		$page = join ( "<br>", $rows );
		
		// Print Header
		echo '<html><head><script type="text/javascript">
					function toggleVisibility(el, src) {
					var v = el.style.display == "block";
					var str = src.innerHTML;
					el.style.display = v ? "none" : "block";
					src.innerHTML = v ? str.replace(/Hide/, "Show") : str.replace(/Show/, "Hide");}
					</script></head><body>';
		
		// Print Index
		echo '<font face="Arial" Size="3"><b>';
		echo 'SmartTemplate Debugger<br>';
		echo '<font size="2"><li>PHP-Script: ' . $_SERVER ['PATH_TRANSLATED'] . '</li><li>Template: ' . $this->filename . '</li></font><hr>';
		echo '<li><a href="#template_code">Template</a></li>';
		echo '<li><a href="#compiled_code">Compiled Template</a></li>';
		echo '<li><a href="#data_code">Data</a></li>';
		echo '</b></font><hr>';
		
		// Print Template
		echo '<a name="template_code"><br><font face="Arial" Size="3"><b>Template:</b>&nbsp;[<a href="javascript:void(\'\');" onclick="toggleVisibility(document.getElementById(\'Template\'), this); return false">Hide Ouptut</a>]</font><br>';
		echo '<table border="0" cellpadding="4" cellspacing="1" width="100%" bgcolor="#C6D3EF"><tr><td bgcolor="#F0F0F0"><pre id="Template" style="display:block">';
		echo $page;
		echo '</pre></td></tr></table>';
		
		// Print Compiled Template
		if (@include_once ("class.smarttemplateparser.php")) {
			$parser = new SmartTemplateParser ( $this->filename );
			$compiled = $parser->compile ();
			echo '<a name="compiled_code"><br><br><font face="Arial" Size="3"><b>Compiled Template:</b>&nbsp;[<a href="javascript:void(\'\');" onclick="toggleVisibility(document.getElementById(\'Compiled\'), this); return false">Hide Ouptut</a>]</font><br>';
			echo '<table border="0" cellpadding="4" cellspacing="1" width="100%" bgcolor="#C6D3EF"><tr><td bgcolor="#F0F0F0"><pre id="Compiled" style="display:block">';
			highlight_string ( $compiled );
			echo '</pre></td></tr></table>';
		} else {
			exit ( "SmartTemplate Error: Cannot find class.smarttemplateparser.php; check SmartTemplate installation" );
		}
		
		// Print Data
		echo '<a name="data_code"><br><br><font face="Arial" Size="3"><b>Data:</b>&nbsp;[<a href="javascript:void(\'\');" onclick="toggleVisibility(document.getElementById(\'Data\'), this); return false">Hide Ouptut</a>]</font><br>';
		echo '<table border="0" cellpadding="4" cellspacing="1" width="100%" bgcolor="#C6D3EF"><tr><td bgcolor="#F0F0F0"><pre id="Data" style="display:block">';
		echo $this->vardump ( $vars );
		echo '</pre></td></tr></table></body></html>';
	}
	
	/**
	 * Insert Hide/Show Layer Switch
	 *
	 * @param string $suffix Additional Text
	 * @desc Insert Hide/Show Layer Switch
	 */
	function toggleview($suffix = '') {
		global $spancnt;
		
		$spancnt ++;
		if ($suffix) {
			$suffix .= ':';
		}
		$ret = '[' . $suffix . '<a href="javascript:void(\'\');" onclick="toggleVisibility(document.getElementById(\'Block' . $spancnt . '\'), this); return false">Hide Block</a>]<span id="Block' . $spancnt . '" style="display:block">';
		return $ret;
	}
	
	/**
	 * Create Title Text
	 *
	 * @param string $value Content
	 * @desc Create Title Text
	 */
	function tip($value) {
		if (empty ( $value )) {
			return "[NULL]";
		} else {
			$ret = htmlentities ( substr ( $value, 0, 200 ) );
			return $ret;
		}
	}
	
	/**
	 * Recursive Variable Display Output
	 *
	 * @param mixed $var Content
	 * @param int $depth Incremented Indent Counter for Recursive Calls
	 * @return string Variable Content
	 * @access private
	 * @desc Recursive Variable Display Output
	 */
	function vardump($var, $depth = 0) {
		if (is_array ( $var )) {
			$result = "Array (" . count ( $var ) . ")<BR>";
			foreach ( array_keys ( $var ) as $key ) {
				$result .= $this->tab [$depth] . "<B>$key</B>: " . $this->vardump ( $var [$key], $depth + 1 );
			}
			return $result;
		} else {
			$ret = htmlentities ( $var ) . "<BR>";
			return $ret;
		}
	}
	
	/**
	 * Splits Template-Style Variable Names into an Array-Name/Key-Name Components
	 *
	 * @param string $tag Variale Name used in Template
	 * @return array  Array Name, Key Name
	 * @access private
	 * @desc Splits Template-Style Variable Names into an Array-Name/Key-Name Components
	 */
	function var_name($tag) {
		$parent_level = 0;
		while ( substr ( $tag, 0, 7 ) == 'parent.' ) {
			$tag = substr ( $tag, 7 );
			$parent_level ++;
		}
		if (substr ( $tag, 0, 4 ) == 'top.') {
			$ret = array ('_stack[0]', substr ( $tag, 4 ) );
			return $ret;
		} elseif ($parent_level) {
			$ret = array ('_stack[$_stack_cnt-' . $parent_level . ']', $tag );
			return $ret;
		} else {
			$ret = array ('_obj', $tag );
			return $ret;
		}
	}
	
	/**
	 * Highlight HTML Source
	 *
	 * @param string $code HTML Source
	 * @return string Hightlighte HTML Source
	 * @access private
	 * @desc Highlight HTML Source
	 */
	function highlight_html($code) {
		$code = htmlentities ( $code );
		$code = preg_replace ( '/([a-zA-Z_]+)=/', '<font color="#FF0000">$1=</font>', $code );
		$code = preg_replace ( '/(&lt;[\/a-zA-Z0-9&;]+)/', '<font color="#0000FF">$1</font>', $code );
		$code = str_replace ( '&lt;!--', '<font color="#008080">&lt;!--', $code );
		$code = str_replace ( '--&gt;', '--&gt;</font>', $code );
		$code = preg_replace ( '/[\r\n]+/', "\n", $code );
		return $code;
	}
}
?>